﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;

namespace UniPie
{
	/// <summary>
	/// The PieMenu user control.
	/// </summary>
	public partial class PieMenu : UserControl , INotifyPropertyChanged
	{

		/// <summary>
		/// It's for storing the previously selected slice in order to restore
		/// its default state in spite of not sending/receiving any events.
		/// </summary>
		private int PreviousSelectedSliceIndex = -1;

		/// <summary>
		/// The number of slices present in the pie menu.
		/// TODO: It should be a variable to handle different pie menu types later on.
		/// </summary>
		private const int NumberOfSlices = 17;

//##################################################################
#region Properties

		/// <summary>
		/// SliceDefinitions property.
		/// 
		/// The list of SliceDefinition instances which will drive the PieSlice controls.
		/// </summary>
		[Category("Common")]
		public ObservableCollection<SliceDefinition> SliceDefinitions
		{
			get { return (ObservableCollection<SliceDefinition>)this.GetValue(SliceDefinitionsProperty); }
			set
			{
				this.SetValue(SliceDefinitionsProperty, value);
				HandlePropertyChanged("SliceDefinitions");
			}
		}

		public static readonly DependencyProperty SliceDefinitionsProperty = DependencyProperty.Register(
			"SliceDefinitions",
			typeof(ObservableCollection<SliceDefinition>),
			typeof(PieMenu),
			new UIPropertyMetadata(PieMenu.SliceDefinitionsChanged));

		private static void SliceDefinitionsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			PieMenu pm = d as PieMenu;

			if (pm != null)
			{
				pm.HandlePropertyChanged(e.Property.ToString());
			}
		}

		/// <summary>
		/// SelectedSliceIndex property.
		/// 
		/// Gets/sets the index of the currently selected slice.
		/// </summary>
		[Category("Common")]
		public int SelectedSliceIndex
		{
			get { return (int)this.GetValue(SelectedSliceIndexProperty); }
			set
			{
				if (value != this.SelectedSliceIndex)
				{
					this.SetValue(SelectedSliceIndexProperty, value);
					HandlePropertyChanged("SelectedSliceIndex");
				}
			}
		}

		public static readonly DependencyProperty SelectedSliceIndexProperty = DependencyProperty.Register(
			"SelectedSliceIndex",
			typeof(int),
			typeof(PieMenu),
			new UIPropertyMetadata(PieMenu.SelectedSliceIndexChanged));

		private static void SelectedSliceIndexChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
		}

		/// <summary>
		/// SelectedSlice property.
		/// 
		/// The SliceDefinition of the currently selected slice. Databound to the slice editing UI.
		/// </summary>
		[Category("Common")]
		public SliceDefinition SelectedSliceDefinition
		{
			get { return (SliceDefinition)this.GetValue(SelectedSliceDefinitionProperty); }
			set
			{
				if (value != this.SelectedSliceDefinition)
				{
					this.SetValue(SelectedSliceDefinitionProperty, value);
					HandlePropertyChanged("SelectedSliceDefinition");
				}
			}
		}

		public static readonly DependencyProperty SelectedSliceDefinitionProperty = DependencyProperty.Register(
			"SelectedSliceDefinition",
			typeof(SliceDefinition),
			typeof(PieMenu),
			new UIPropertyMetadata(PieMenu.SelectedSliceDefinitionChanged));

		private static void SelectedSliceDefinitionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			PieMenu pm = d as PieMenu;

			if (pm != null)
			{
				pm.HandlePropertyChanged(e.Property.ToString());
			}
		}

		/// <summary>
		/// Preview mode property.
		/// 
		/// Defines if the pie menu is in preview (edit) mode where
		/// - There is always exactly one selected slice. (In normal mode there could be none.)
		/// - Slices are selected by clicking on them. (As opposed to hover.)
		/// - Slice selection looks different.
		/// - The center slice is not visible.
		/// </summary>
		[Category("Common")]
		public bool PreviewMode
		{
			get { return (bool)this.GetValue(PreviewModeProperty); }
			set
			{
				if (value != this.PreviewMode)
				{
					this.SetValue(PreviewModeProperty, value);
					HandlePropertyChanged("PreviewMode");
				}
			}
		}

		public static readonly DependencyProperty PreviewModeProperty = DependencyProperty.Register(
			"PreviewMode",
			typeof(bool),
			typeof(PieMenu),
			new UIPropertyMetadata(PieMenu.PreviewModeChanged));

		private static void PreviewModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			PieMenu pm = d as PieMenu;

			if (pm != null)
			{
				pm.HandlePropertyChanged(e.Property.ToString());
			}
		}
#endregion

//##################################################################
#region Event handlers
		/// <summary>
		/// 
		/// </summary>
		public event PropertyChangedEventHandler PropertyChanged;

		/// <summary>
		/// Notifies the property changed.
		/// </summary>
		/// <param name="property">The property.</param>
		public void HandlePropertyChanged(string property)
		{
			PieSlice ps;

			if (PropertyChanged != null)
			{
				PropertyChanged(this, new PropertyChangedEventArgs(property));
			}

			switch (property)
			{
				case "PreviewMode":
					if (PreviewMode)
					{
						// Automatically select Slice #1.
						ps = (PieSlice)PieContainer.Children[1];
						if (ps != null)
						{
							ps.IsSelected = true;
						}
						else
						{
							Log.i("PieMenu.PieContainer.Children[1] is not a PieSlice!");
						}
					}
					break;

				case "SelectedSliceIndex":
					if (SelectedSliceIndex == -1 || SliceDefinitions == null)
					{
						SelectedSliceDefinition = null;
					}
					else
					{
						// TODO: It will still fail if the number of slice definitions is not proper.
						SelectedSliceDefinition = SliceDefinitions[SelectedSliceIndex];
					}
					break;

				case "SelectedSliceDefinition":
					break;
				default:
					break;
			}
		}

		/// <summary>
		/// Notifies when IsEnabled changed.
		/// </summary>
		private void HandleIsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
		{
			if (IsEnabled)
			{
				VisualStateManager.GoToState(this, "Default", false);
			}
			else
			{
				VisualStateManager.GoToState(this, "Disabled", false);
			}
		}

		/// <summary>
		/// Notifies the property changed.
		/// </summary>
		/// <param name="property">The property.</param>
		private void HandleDependencyPropertyChanged(DependencyPropertyChangedEventArgs e)
		{
		}

		/// <summary>
		/// Indicates a property changed in a slice.
		/// </summary>
		/// <param name="property">The property.</param>
		private void HandleSlicePropertyChanged(object sender, PropertyChangedEventArgs e)
		{
			PieSlice ps = sender as PieSlice;

			switch (e.PropertyName)
			{
				case "IsSelected":
					if (PreviewMode)
					{
						if (ps.IsSelected)
						{
							// Here we are deriving the index of the slice from it's name.
							// TODO: Maybe index in the visual tree would be a slightly more elegant way to do this.
							string[] numbers = Regex.Split(ps.Name, @"\D+");
							if (!string.IsNullOrEmpty(numbers[1]))
							{
								PreviousSelectedSliceIndex = SelectedSliceIndex;
								SelectedSliceIndex = int.Parse(numbers[1]);
							}

							if (PreviousSelectedSliceIndex != -1)
							{
								ps = (PieSlice)PieContainer.Children[PreviousSelectedSliceIndex];
								if (ps != null)
								{
									ps.IsSelected = false;
								}
							}
						}
					}
					else // Not PreviewMode
					{
						if (ps.IsSelected && ps.Definition.IsEnabled)
						{
							string[] numbers = Regex.Split(ps.Name, @"\D+");
							if (!string.IsNullOrEmpty(numbers[1]))
							{
								SelectedSliceIndex = int.Parse(numbers[1]);
							}
						}
						else
						{
							SelectedSliceIndex = -1;
						}
					}
					break;

				case "Definition":
					break;
			}
		}

#endregion

		/// <summary>
		/// Updates the SelectedSliceDefinition.
		/// </summary>
		public void UpdateSelectedSliceDefinition()
		{
			PieSlice ps;

			if (PreviewMode)	// We only need update while in preview mode (setting up the pie menu);
			{
				if (SliceDefinitions != null && SelectedSliceIndex > 0)
				{
					SelectedSliceDefinition = SliceDefinitions[SelectedSliceIndex];
				}

				ps = (PieSlice)PieContainer.Children[SelectedSliceIndex];
				if (ps != null)
				{
					ps.UpdateVisualStates();
				}
			}
		}

		/// <summary>
		/// Updates the visual states in all slices.
		/// </summary>
		public void UpdateAllSliceVisualStates()
		{
			PieSlice ps;

			for (int i = 1; i < NumberOfSlices; i++)
			{
				ps = (PieSlice)PieContainer.Children[i];
				if (ps != null)
				{
					ps.UpdateVisualStates();
				}
			}
		}

		public PieMenu()
		{
			InitializeComponent();
		}
	}
}
